package immodels

import (
	"context"
	"github.com/zeromicro/go-zero/core/stores/mon"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo/options"
)

var _ ChatLogModel = (*customChatLogModel)(nil)

type (
	// ChatLogModel is an interface to be customized, add more methods here,
	// and implement the added methods in customChatLogModel.
	ChatLogModel interface {
		chatLogModel
		ListBySendTime(ctx context.Context, conversationId string, beginTime, endTime, limit int64) ([]*ChatLog, error)
	}

	customChatLogModel struct {
		*defaultChatLogModel
	}
)

// NewChatLogModel returns a model for the mongo.
func NewChatLogModel(url, db, collection string) ChatLogModel {
	conn := mon.MustNewModel(url, db, collection)
	return &customChatLogModel{
		defaultChatLogModel: newDefaultChatLogModel(conn),
	}
}

func (m *customChatLogModel) ListBySendTime(ctx context.Context, conversationId string, beginTime, endTime, limit int64) ([]*ChatLog, error) {
	var data []*ChatLog

	// 用于指定查询选项
	opt := options.FindOptions{
		Limit: &DefaultChatLogLimit, // 设置了查询结果的最大返回文档数为 DefaultChatLogLimit
		Sort: bson.M{ // 指定了查询结果应根据 sendTime 字段降序排列
			"sendTime": -1,
		},
	}
	if limit > 0 { // 如果传入的 limit 参数大于 0，则使用这个新的限制值覆盖默认的限制值
		opt.Limit = &limit
	}

	// 构建查询过滤器
	filter := bson.M{
		"conversationId": conversationId,
	}

	if beginTime > 0 {
		// 加载历史消息：
		// 用户已加载了当前视图中一批消息，最早的消息时间戳是endTime，当用户向上滚动到顶部时，触发"加载更早消息"
		// 系统需要加载比当前最早消息更早的一批消息，beginTime是上一次查询时使用的边界
		// 假设当前显示2023-05-10 14:00到2023-05-10 15:00的消息
		// endTime = 2023-05-10 14:00 (当前视图中最早的消息时间)，beginTime = 2023-05-10 13:00 (上次查询的边界)
		// 查询会返回13:00-14:00之间的所有消息
		filter["sendTime"] = bson.M{
			"$gt":  beginTime,
			"$lte": endTime,
		}
	} else {
		// 用户首次打开对话，需要加载最新的一批消息
		// endTime通常设为当前时间，不设下界，只查询比endTime更早的消息，配合limit参数，限制返回数量
		// 用户打开聊天窗口时，endTime = 当前时间 2023-05-10 15:00，查询返回比15:00更早的最新20条消息
		filter["sendTime"] = bson.M{
			"$lt": endTime,
		}
	}
	err := m.conn.Find(ctx, &data, filter, &opt)
	switch err {
	case nil:
		return data, nil
	case mon.ErrNotFound:
		return nil, ErrNotFound
	default:
		return nil, err
	}
}
